/*
 * Copyright 2002-2006 the original author or authors. Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language governing permissions and limitations under the
 * License.
 */
package pikes.samples.countries.ui.peak;

import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.servlet.support.RequestContext;

import pikes.ecma.FormattingScriptPrinter;
import pikes.ecma.SyntaxTreeVisitor;
import pikes.html.xhtml.Anchor;
import pikes.html.xhtml.Head;
import pikes.html.xhtml.Inline;
import pikes.html.xhtml.Link;
import pikes.html.xhtml.Meta;
import pikes.html.xhtml.Span;
import pikes.peak.ApplicationContextMessageProvider;
import pikes.peak.ClientSideScript;
import pikes.peak.ComponentHolder;
import pikes.peak.Composite;
import pikes.peak.CookieLocaleURIParameter;
import pikes.peak.CookieThemeURIParameter;
import pikes.peak.InlineComponent;
import pikes.peak.InternalURI;
import pikes.peak.Label;
import pikes.peak.ListComposite;
import pikes.peak.Page;
import pikes.peak.Picture;
import pikes.peak.PikesPeakPage;
import pikes.peak.RequestProperty;
import pikes.peak.ScriptFormattingConfiguration;
import pikes.peak.SelfURI;
import pikes.peak.Paragraph;
import pikes.peak.TextGroupStyle;
import pikes.peak.VisibilityCondition;
import pikes.uri.URI;

public abstract class BaseCountriesPage extends PikesPeakPage {

	public static Set<Locale> AVAILABLE_LOCALES = new LinkedHashSet<Locale>();

	public static Set<String> AVAILABLE_THEMES = new LinkedHashSet<String>();

	static {
		AVAILABLE_LOCALES.add(Locale.US);
		AVAILABLE_LOCALES.add(Locale.FRANCE);
		AVAILABLE_LOCALES.add(Locale.GERMANY);

		AVAILABLE_THEMES.add("spring");
		AVAILABLE_THEMES.add("blue");
		AVAILABLE_THEMES.add("white");
		AVAILABLE_THEMES.add("none");
	}

	protected ScriptFormattingConfiguration prettyJavaScript = new ScriptFormattingConfiguration() {
		public SyntaxTreeVisitor createScriptSyntaxVisitor(Appendable appendable) {
			FormattingScriptPrinter scriptPrinter = new FormattingScriptPrinter(appendable);
			scriptPrinter.setSpaceBeforeOpeningBrace(true);
			scriptPrinter.setStartBlockInNewLine(true);
			scriptPrinter.setSpaceBeforeAssignmentOperator(true);
			scriptPrinter.setSpaceAfterAssignmentOperator(true);
			scriptPrinter.setSpaceBeforeBinaryOperator(true);
			scriptPrinter.setSpaceAfterBinaryOperator(true);
			scriptPrinter.setNewLineAfterEndOfStatement(true);
			scriptPrinter.setIndent(true);
			return scriptPrinter;
		}
	};

	protected void buildNavigation(Composite navigationPanel) {

		ListComposite menuBar = new ListComposite();

		Set<String> linkKeys = new LinkedHashSet<String>();
		addLinkKeys(linkKeys);

		for (String linkKey : linkKeys) {
			menuBar.add(new ComponentHolder(new CountriesMenu(new Label("nav." + linkKey), new InternalURI("lnk."
					+ linkKey), "nav." + linkKey + ".info")));
		}

		for (String theme : AVAILABLE_THEMES) {
			menuBar.add(new ComponentHolder(new ThemeDependentVisibility(theme), new CountriesMenu(new Label("nav.css."
					+ theme), new SelfURI(new CookieThemeURIParameter(theme)), "nav.css." + theme + ".info")));
		}

		for (Locale availableLocale : AVAILABLE_LOCALES) {
			menuBar.add(new ComponentHolder(new LocaleDependentVisibility(availableLocale), new CountriesMenu(
					new Picture(new InternalURI("img." + availableLocale.getLanguage()), "nav.lang."
							+ availableLocale.getLanguage()),
					new SelfURI(new CookieLocaleURIParameter(availableLocale)), "nav.lang."
							+ availableLocale.getLanguage() + ".info")));
		}

		navigationPanel.add(menuBar);
	}

	abstract protected void buildContent(Composite content);

	protected void buildInContent(Composite inContent) {

		Composite navigation = new Composite("nav");
		buildNavigation(navigation);
		inContent.add(navigation);

		inContent.add(new Composite("top"));

		Composite content = new Composite("content");
		content.add(new Paragraph("app.short", TextGroupStyle.LEVEL_1));
		buildContent(content);

		inContent.add(content);

		Composite footerPanel = new Composite("footer");
		buildNavigation(footerPanel);
		inContent.add(footerPanel);
	}

	@Override
	final protected void buildPage(Page page) {

		page.setTitle(getTitle());

		Composite pageContent = new Composite("pageContent");

		Composite in = new Composite("inContent");
		pageContent.add(in);

		buildInContent(in);

		page.add(pageContent);
	}

	abstract protected String getTitle();

	@Override
	final protected void buildHeader(Head head, Map model, HttpServletRequest request) throws Exception {
		RequestContext requestContext = new RequestContext(request);

		List<ClientSideScript> scripts = new LinkedList<ClientSideScript>();

		setupClientSideScripts(scripts, model, requestContext);

		for (ClientSideScript clientSideScript : scripts) {
			head.addMisc(clientSideScript.createHtml(model, requestContext));
		}

		Meta meta = new Meta(getContentType());
		meta.setHTTPEquiv("Content-Type");
		head.addMisc(meta);

		Link link = new Link();
		link.setRel("stylesheet");
		link.setHRef(requestContext.getContextPath() + requestContext.getThemeMessage("css"));
		link.setType("text/css");
		head.addMisc(link);
	}

	protected void setupClientSideScripts(Collection<ClientSideScript> scripts, Map model, RequestContext requestContext)
			throws Exception {
		// by default no scripts

	}

	protected void addLinkKeys(Set<String> linkKeys) {
		linkKeys.add("home");
		linkKeys.add("countries.main");
		linkKeys.add("config");
		linkKeys.add("javadoc");
	}
}

class CountriesMenu implements InlineComponent {
	private InlineComponent visual = null;

	private RequestProperty<CharSequence> tooltip = null;

	private RequestProperty<URI> uri = null;

	public CountriesMenu(InlineComponent visualRepresentation, RequestProperty<URI> uri, String toolTip) {
		this.visual = visualRepresentation;
		this.uri = uri;
		this.tooltip = new ApplicationContextMessageProvider(toolTip);
	}

	public Inline createHtml(Map model, RequestContext requestContext) throws Exception {
		Anchor link = new Anchor(uri.getValue(model, requestContext), visual.createHtml(model, requestContext));
		link.add(new Span(tooltip.getValue(model, requestContext)));
		return link;
	}
}

class LocaleDependentVisibility implements VisibilityCondition {

	private Locale locale = null;

	public LocaleDependentVisibility(Locale locale) {
		this.locale = locale;
	}

	public boolean isTrue(Map model, RequestContext requestContext) throws Exception {
		return !locale.equals(requestContext.getLocale());
	}
}

class ThemeDependentVisibility implements VisibilityCondition {

	private String theme = null;

	public ThemeDependentVisibility(String theme) {
		this.theme = theme;
	}

	public boolean isTrue(Map model, RequestContext requestContext) throws Exception {
		return !theme.equals(requestContext.getTheme().getName());
	}
}